﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml;
using Catsland.Core;
using System.ComponentModel;

namespace Catsland.CatsPlugin
{
	public class CharacterController : CatComponent
	{
		// wants
		public bool m_wantLeft;
		public bool m_wantRight;
		public bool m_wantUp;
		public bool m_wantDown;
		public bool m_wantJump;
		public bool m_wantRun;
		public bool m_wantClimbUp;
		public bool m_wantClimbDown;
		public bool m_wantAttack;

		public bool m_wantTalk;
		int m_talkInterval = 0;
		int m_talkCooldown = 1000;
		// moving attribute
		public bool m_isRunning;

		private float m_walkingSpeed = 0.4f;
        [CategoryAttribute("Movement")]
        public float WalkingSpeed
        {
            get { return m_walkingSpeed; }
            set { m_walkingSpeed = value; }
        }

		private float m_runningSpeed = 1.0f;
        [CategoryAttribute("Movement")]
        public float RunningSpeed
        {
            get { return m_runningSpeed; }
            set { m_runningSpeed = value; }
        }

		public float m_climbingSpeed = 0.3f;
		public Vector2 m_XYspeed;
		public float m_Zspeed;

        private float m_damping;
        [CategoryAttribute("Movement")]
        public float Damping
        {
            get { return m_damping; }
            set { m_damping = value; }
        }

		private float m_gravity;
        [CategoryAttribute("Movement")]
        public float Gravity
        {
            get { return m_gravity; }
            set { m_gravity = value; }
        }

		public bool m_isOnGround;
		public bool m_canClimb;

		private int m_attackCoolDown = 500;
        [CategoryAttribute("Attack")]
        public int AttackCooldown
        {
            get { return m_attackCoolDown; }
            set { m_attackCoolDown = value; }
        }
		public int m_attackInterval = 0;

		public CharacterController(GameObject gameObject)
			: base(gameObject)
		{

		}

		public override CatComponent CloneComponent(GameObject gameObject)
		{
			CharacterController characterController = new CharacterController(gameObject);
			return characterController;
		}

		public override void Initialize(Scene scene)
		{
			base.Initialize(scene);

			m_wantLeft = false;
			m_wantRight = false;
			m_wantUp = false;
			m_wantDown = false;
			m_wantJump = false;
			m_wantRun = false;
			m_wantClimbUp = false;
			m_wantClimbDown = false;

			m_isRunning = false;
			m_isOnGround = true;
			m_canClimb = false;
			m_XYspeed = new Vector2(0.0f, 0.0f);
			m_damping = 0.8f;
			m_gravity = -9.8f;

			m_wantTalk = false;
		}


		public override void Update(int timeLastFrame)
		{
			if (Enable == false)
			{
				return;
			}

			base.Update(timeLastFrame);
			float timeLastFrameSecond = timeLastFrame / 1000.0f;
			bool old_isOnGround = m_isOnGround;

            Animator animator = (Animator)m_gameObject.GetComponent(typeof(Animator).Name);
            ParticleEmitter particleEmitter = (ParticleEmitter)m_gameObject.GetComponent(typeof(ParticleEmitter).Name);

			if (m_isOnGround || m_canClimb)
			{
				if (m_wantRun)
				{
					m_isRunning = true;
				}
				else
				{
					m_isRunning = false;
				}

				Vector2 wantXYDirection = new Vector2(0.0f, 0.0f);

				if (m_wantLeft)
				{
					wantXYDirection += new Vector2(-1.0f, 0.0f);
				}
				if (m_wantRight)
				{
					wantXYDirection += new Vector2(1.0f, 0.0f);
				}
				if (m_wantUp)
				{
					wantXYDirection += new Vector2(0.0f, 1.0f);
				}
				if (m_wantDown)
				{
					wantXYDirection += new Vector2(0.0f, -1.0f);
				}
				if (wantXYDirection.LengthSquared() > 0.0f)
				{
					wantXYDirection.Normalize();
				}

				if (m_talkInterval > 0)
				{
					m_talkInterval -= timeLastFrame;
				}
				if(m_attackInterval > 0)
				{
					m_attackInterval -= timeLastFrame;
				}

				if (m_wantAttack && m_attackInterval <= 0)
				{
					m_attackInterval = m_attackCoolDown;

					GameObject attackMessage = new GameObject();
                    
					attackMessage.Position = m_gameObject.Position +
                        (animator.Mirror ? 1.0f : -1.0f) * new Vector2(0.2f, 0.0f);
                    attackMessage.Height = m_gameObject.Height;
					Collider detector = new Collider(attackMessage);
					detector.AdvantageTrigger = false;
					detector.ColliderTypeAttribute = Collider.ColliderType.PositiveTrigger;
					CatsBoundingBox boundingBox = new CatsBoundingBox();
					boundingBox.m_XBound = new Vector2(-0.1f, 0.1f);
					boundingBox.m_YBound = new Vector2(-0.1f, 0.1f);
					boundingBox.m_heightRange = new Vector2(0.0f, 0.2f);
					boundingBox.UpdateBoundFromRectangle();
					detector.m_rootBounding = boundingBox;
					AttackInvoke attackInvoker = new AttackInvoke();
                    attackInvoker.SetOwner(m_gameObject);
					detector.m_triagerInvoker = attackInvoker;
					attackMessage.AddComponent(typeof(Collider).Name,detector);
					//talkMessage.Initialize(Mgr<Scene>.Singleton);
                    Mgr<Scene>.Singleton._gameObjectList.AddItem(attackMessage._guid, attackMessage);

					SelfDestroyer selfDestroyer = new SelfDestroyer(attackMessage);
					selfDestroyer.m_time = 500;
					attackMessage.AddComponent(typeof(SelfDestroyer).Name, selfDestroyer);
				}

				if (m_wantTalk && m_talkInterval <= 0)
				{
					m_talkInterval = m_talkCooldown;
					GameObject talkMessage = new GameObject();
					talkMessage.Position = m_gameObject.Position + 
						(animator.Mirror ? 1.0f : -1.0f) * new Vector2(0.2f, 0.0f);

					Collider detector = new Collider(talkMessage);
					detector.AdvantageTrigger = false;
					detector.ColliderTypeAttribute = Collider.ColliderType.PositiveTrigger;
					CatsBoundingBox boundingBox = new CatsBoundingBox();
					boundingBox.m_XBound = new Vector2(-0.1f, 0.1f);
					boundingBox.m_YBound = new Vector2(-0.1f, 0.1f);
					boundingBox.m_heightRange = new Vector2(0.0f, 0.2f);
					boundingBox.UpdateBoundFromRectangle();
					detector.m_rootBounding = boundingBox;
					DialogTrigger dialogTrigger = new DialogTrigger();
                    dialogTrigger.SetOwner(m_gameObject);
					detector.m_triagerInvoker = dialogTrigger;

					talkMessage.AddComponent(typeof(Collider).Name, detector);
					//talkMessage.Initialize(Mgr<Scene>.Singleton);
                    Mgr<Scene>.Singleton._gameObjectList.AddItem(talkMessage._guid, talkMessage);

					SelfDestroyer selfDestroyer = new SelfDestroyer(talkMessage);
					selfDestroyer.m_time = 500;
					talkMessage.AddComponent(typeof(SelfDestroyer).Name, selfDestroyer);
					//selfDestroyer.Initialize(Mgr<Scene>.Singleton);
				}

				if (m_wantJump)
				{
					m_Zspeed += 3.0f;
				}


				// speed
				float velocity = 0.0f;
				if (m_canClimb)
				{
					velocity = m_climbingSpeed;
				}
				else
				{
					velocity = m_isRunning ? m_runningSpeed : m_walkingSpeed;
				}
				Vector2 desiredSpeedXY = wantXYDirection * velocity;
				m_XYspeed = (m_damping) * m_XYspeed + desiredSpeedXY * (1.0f - m_damping);
			}

			if (m_canClimb)
			{
				if (m_wantClimbUp)
				{
					m_Zspeed = m_climbingSpeed;
				}
				else if (m_wantClimbDown)
				{
					m_Zspeed = -m_climbingSpeed;
				}
				else
				{
					m_Zspeed = 0.0f;
				}
			}
			else
			{
				// apply gravity
				m_Zspeed += m_gravity * timeLastFrameSecond;
			}
			
			// calculate destination
			Vector2 destionation = m_gameObject.Position + m_XYspeed * timeLastFrameSecond;
			float destination_height = m_gameObject.Height + m_Zspeed * timeLastFrameSecond;

			// TODO: check whether destination validate
			CatsCollider collider = (CatsCollider)m_gameObject.GetComponent(typeof(CatsCollider).Name);
			if (collider != null)
			{
				Collider result = Mgr<ColliderList>.Singleton.JudgeCollide(collider.m_collider.m_rootBounding,
					destionation, m_gameObject.Height, collider.m_collider);
				if (result != null)
				{
					destionation = m_gameObject.Position;
				}

				result = Mgr<ColliderList>.Singleton.JudgeCollide(collider.m_collider.m_rootBounding,
					destionation, destination_height, collider.m_collider);
				if (result != null)
				{
					destination_height = m_gameObject.Height;
					if (old_isOnGround == false)
					{
						particleEmitter.OneShot(10);
					}
					m_isOnGround = true;
					
					m_Zspeed = 0.0f;
				}
				else
				{
					m_isOnGround = false;
				}
			}

			if (destination_height < 0.0f)
			{
				destination_height = 0.0f;
				if (old_isOnGround == false)
				{
					particleEmitter.OneShot(10);
				}
				m_isOnGround = true;
				m_Zspeed = 0.0f;
			}
			
			// scene bound
			Vector3 destination_pos3 = Mgr<Scene>.Singleton.GetInBoundPosition(destionation, destination_height);

			// apply position
			m_gameObject.Position = new Vector2(destination_pos3.X, destination_pos3.Y);
			m_gameObject.Height = destination_pos3.Z;

			// apply animation
			if (animator != null)
			{
				// left or right
				if (m_XYspeed.X < -0.01f)
				{
					animator.Mirror = false;
				}
				if (m_XYspeed.X > 0.01f)
				{
					animator.Mirror = true;
				}

				if (m_canClimb)
				{
					float climbThreshold = m_climbingSpeed * 0.5f;
					if (m_Zspeed > climbThreshold || m_Zspeed < -climbThreshold
						|| m_XYspeed.LengthSquared() > climbThreshold * climbThreshold)
					{
						animator.fadeToAnimation("climbing");
					}
					else
					{
						animator.Pause();
					}
				}
				else if (m_isOnGround)
				{
					// animation clip
					float walkRunThreshold = (m_walkingSpeed + m_runningSpeed) * 0.5f;
					float standWalkThreshold = m_walkingSpeed * 0.5f;
					// TODO: speed should divid cos
					if (m_XYspeed.LengthSquared() > walkRunThreshold * walkRunThreshold)
					{
						animator.fadeToAnimation("running");
						particleEmitter.m_isEmitting = true;
					}
					else if (m_XYspeed.LengthSquared() > standWalkThreshold * standWalkThreshold)
					{
						animator.fadeToAnimation("walking");
                        particleEmitter.m_isEmitting = false;
					}
					else
					{
						animator.fadeToAnimation("standing");
                        particleEmitter.m_isEmitting = false;
					}
				}
				else
				{
					particleEmitter.m_isEmitting = false;
					// animation clip
					if (m_Zspeed > 0.05f)
					{
						animator.CheckPlayAnimation("jumpingUp");
					}
					else if(m_Zspeed < -0.5f)
					{
						animator.CheckPlayAnimation("jumpingDown");
					}
				}
			}
		}

		public override bool SaveToNode(XmlNode node, XmlDocument doc)
		{
			XmlElement characterController = doc.CreateElement("CharacterController");
			node.AppendChild(characterController);
			return true;
		}

        public override void ConfigureFromNode(XmlElement node, Scene scene, GameObject gameObject)
        {
            base.ConfigureFromNode(node, scene, gameObject);
            return;
        }
	}
}
